package org.alternativedev.wo42.map;

import java.util.ArrayList;
import java.util.Iterator;

import org.alternativedev.wo42.Game;
import org.alternativedev.wo42.Settings;
import org.alternativedev.wo42.gameobjects.BlockingField;
import org.alternativedev.wo42.gameobjects.Entity;
import org.alternativedev.wo42.gameobjects.Item;
import org.alternativedev.wo42.gameobjects.entities.EntityNPC;
import org.alternativedev.wo42.gameobjects.entities.EntityPlayer;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Music;
import org.newdawn.slick.SlickException;

/**
 * Class describing a whole Level including the Player and so on
 * 
 * @author janni-futz
 * 
 */
public abstract class Map extends TiledMapPlus {

	public Music mainMusic;
	public Music deathMusic;
	/** ArrayList containing all the Entities on the Map */
	private ArrayList<Entity> entities = new ArrayList<Entity>();

	/** ArrayList containing all the Items on the Map */
	private ArrayList<Item> items = new ArrayList<Item>();

	/** ArrayList containing all the BlockingFields on the Map */
	private ArrayList<BlockingField> blocks = new ArrayList<BlockingField>();

	/** The Main PlayerEntity on the Map */
	private EntityPlayer player;

	/** Scroller handling the Scrolling of the Map */
	private Scroll scroller;
	/**
	 * The position on the X-Axis
	 * 
	 * @see #getMapX()
	 */
	private int mapX = 0;

	/**
	 * The position on the Y-Axis
	 * 
	 * @see #getMapY()
	 */
	private int mapY = 0;

	/** The players' spawnpoint */
	private int playerSpawnX, playerSpawnY;

	/**
	 * Constructs a new map
	 * 
	 * @param ref
	 *            tua mater
	 * @throws SlickException
	 */
	public Map(String ref, String mainMusicRef, String deathMusicRef)
			throws SlickException {
		super(ref, "data");

		int id = this.getObjectGroupID("Collision Objects");
		for (int i = 0; i < this.getObjectCount(id); i++) {
			blocks.add(new BlockingField(this.getObjectX(id, i), this
					.getObjectY(id, i), this.getObjectWidth(id, i), this
					.getObjectHeight(id, i)));
		}

		id = this.getObjectGroupID("Player");

		playerSpawnX = this.getObjectX(id, 0);
		playerSpawnY = this.getObjectY(id, 0);

		this.player = new EntityPlayer(playerSpawnX, playerSpawnY);

		setScroller(new Scroll());
		this.mainMusic = new Music(mainMusicRef);
		this.deathMusic = new Music(deathMusicRef);
		this.music = mainMusic;
		music.play();
	}

	public void stopMusic() {
		this.mainMusic.stop();
	}

	public void startMusic() {
		this.music.play();
	}

	public boolean musicPlaying() {
		return this.music.playing();
	}

	/**
	 * Get all Entities on the Map
	 * 
	 * @return all Entities on the Map
	 */
	public ArrayList<Entity> getEntities() {
		return entities;
	}

	/**
	 * Get all Items on the Map
	 * 
	 * @return All Items on the Map
	 */
	public ArrayList<Item> getItems() {
		return items;
	}

	/**
	 * Get the Maps' coordinates on the X-Axis.
	 * 
	 * @return The Maps' coordinates on the X-Axis
	 */
	public int getMapX() {
		return mapX;
	}

	/**
	 * Get the Maps' coordinates on the Y-Axis.
	 * 
	 * @return The Maps' coordinates on the Y-Axis
	 */
	public int getMapY() {
		return mapY;
	}

	/**
	 * Add an Entity to the Map
	 * 
	 * @param ent
	 *            The Entity to add
	 */
	public void addEntity(Entity ent) {
		entities.add(ent);
	}

	/**
	 * Add an Item to the Map
	 * 
	 * @param it
	 *            The Item to add
	 */
	public void addItem(Item it) {
		items.add(it);
	}
	

	/**
	 * Get the PlayerEntity.
	 * 
	 * @return The PlayerEntity
	 */
	public EntityPlayer getPlayer() {
		return player;
	}

	/** The Location the stuff was on before the new scrolling */
	private float xBefore = 0, yBefore = 0;

	/**
	 * Change the Maps' and all Entities, Items' and BLockingFields' position
	 * for scrolling
	 * 
	 * @param x
	 *            Scrolling on the X-Axis
	 * @param y
	 *            Scrolling on the Y-Axis
	 */
	public void scroll(float x, float y) {
		this.setMapX((int) x);
		this.setMapY((int) y);

		Iterator<Entity> itent = entities.iterator();

		while (itent.hasNext()) {
			Entity ent = itent.next();
			if (xBefore < x)
				ent.setPositionX(ent.getPositionX() + 1);
			else if (xBefore > x)
				ent.setPositionX(ent.getPositionX() - 1);
			else
				ent.setPositionX(ent.getPositionX());
			if (yBefore < y)
				ent.setPositionY(ent.getPositionY() + 1);
			else if (yBefore > y)
				ent.setPositionY(ent.getPositionY() - 1);
			else
				ent.setPositionY(ent.getPositionY());
		}

		for (int i = 0; i < this.items.size(); i++) {
			Item item = (Item) this.items.get(i);
			if (xBefore < x)
				item.setPositionX(item.getPositionX() + 1);
			else if (xBefore > x)
				item.setPositionX(item.getPositionX() - 1);
			else
				item.setPositionX(item.getPositionX());
			if (yBefore < y)
				item.setPositionY(item.getPositionY() + 1);
			else if (yBefore > y)
				item.setPositionY(item.getPositionY() - 1);
			else
				item.setPositionY(item.getPositionY());

		}

		for (int i = 0; i < this.blocks.size(); i++) {
			BlockingField blockingField = (BlockingField) this.blocks.get(i);
			if (xBefore < x)
				blockingField.setPositionX((blockingField.getPositionX() + 1));
			else if (xBefore > x)
				blockingField.setPositionX(blockingField.getPositionX() - 1);
			else
				blockingField.setPositionX(blockingField.getPositionX());

			if (yBefore < y)
				blockingField.setPositionY(blockingField.getPositionY() + 1);
			else if (yBefore > y)
				blockingField.setPositionY(blockingField.getPositionY() - 1);
			else
				blockingField.setPositionY(blockingField.getPositionY());

		}
		
		xBefore = x;
		yBefore = y;
	}

	/**
	 * Draw all the Entities. Items, BlockingFields (Debug), the Player and the
	 * map itself onto the screen.
	 * 
	 * @param g
	 *            The Games' Graphics
	 */
	int dieTime = 0;
	private Music music;

	public void drawAll(Graphics g, Game game) {
		Iterator<Entity> itent;
		Iterator<Item> itit;
		this.render(this.mapX, this.mapY);

		player.draw(g, game);

		itent = entities.iterator();

		while (itent.hasNext()) {
			itent.next().draw(g, game);
		}
		itit = items.iterator();

		while (itit.hasNext()) {
			itit.next().draw();
		}

		this.render(this.getMapX(), this.getMapY(), 1);

		itent = entities.iterator();

		while (itent.hasNext()) {
			Entity ent = itent.next();
			if (ent instanceof EntityNPC)
				((EntityNPC) ent).drawSpeechBubble(g, game);
		}

	}

	/**
	 * Get the Maps' Width.
	 * 
	 * @return The Maps' Width
	 */
	public int getMapWidth() {
		return this.getWidth() * this.getTileWidth();
	}

	/**
	 * Get the Maps' Height.
	 * 
	 * @return The Maps' Height
	 */
	public int getMapHeight() {
		return this.getHeight() * this.getTileHeight();
	}

	/**
	 * Detects if the Entity is colliding with a BlockingField or not.
	 * 
	 * @param ent
	 *            The Entitiy to check
	 * @return Returns if the Entity is colliding with a BlockingField or not
	 * @throws SlickException
	 */
	public boolean collisionObjectsCollideWith(Entity ent)
			throws SlickException {
		for (int i = 0; i < this.blocks.size(); i++) {
			BlockingField blockingField = (BlockingField) this.blocks.get(i);

			if (ent.getPoly().intersects(blockingField.getPoly())) {
				return true;
			}

		}
		return false;
	}

	/**
	 * Set the Maps' X-Coordinates
	 * 
	 * @param mapX
	 *            The new coordinates on the X-Axis
	 */
	public void setMapX(int mapX) {
		this.mapX = mapX;
	}

	/**
	 * Set the Maps' Y-Coordinates
	 * 
	 * @param mapY
	 *            The new coordinates on the Y-Axis
	 */
	public void setMapY(int mapY) {
		this.mapY = mapY;
	}

	/**
	 * Convert the Coordinates got from the window (upper left corner = 0) to
	 * Coordinates on the map.
	 * 
	 * @param windowX
	 *            The window Coordinates (x)
	 * @return Coordinates on the map
	 */
	public int convertToMapX(int windowX) {

		return windowX - this.getMapX();

	}

	public int convertToMapY(int windowY) {
		return windowY - this.getMapY();

	}

	public void update(GameContainer gc, int delta, Game game)
			throws SlickException {
		Iterator<Entity> itent = entities.iterator();

		while (itent.hasNext()) {
			Entity ent = itent.next();
			ent.onUpdate(gc, delta, this, game);
		}

		Iterator<Item> itit = items.iterator();

		while (itit.hasNext()) {
			Item it = itit.next();
			it.onUpdate(gc, delta, this, game);
		}

		this.player.onUpdate(gc, delta, this, game);

		if (scroller.isScrollingLeft()
				&& this.getPlayer().getPositionX() <= Settings
						.getPlayerDistance())
			this.scroll(this.getMapX() + 1, this.getMapY());
		else if (scroller.isScrollingRight()
				&& this.getPlayer().getPositionX() >= Settings.getWIDTH()
						- Settings.getPlayerDistance())
			this.scroll(this.getMapX() - 1, this.getMapY());
		else if (scroller.isScrollingDown()
				&& this.getPlayer().getPositionY() >= Settings.getHEIGHT()
						- Settings.getPlayerDistance())
			this.scroll(this.getMapX(), this.getMapY() - 1);
		else if (scroller.isScrollingUp()
				&& this.getPlayer().getPositionY() <= Settings
						.getPlayerDistance())
			this.scroll(this.getMapX(), this.getMapY() + 1);
		else
			this.scroll(this.getMapX(), this.getMapY());

	}

	public Scroll getScroller() {
		return scroller;
	}

	public void setScroller(Scroll scroller) {
		this.scroller = scroller;
	}

	public void onPlayerDeath() throws SlickException {
		if (music != this.deathMusic) {
			this.music.stop();
			this.music = this.deathMusic;
			music.play();
		}
	}

	public void updatePaused(GameContainer gc, int delta, Game game)
			throws SlickException {

	}

}
